class
	TYPER
create
	make
feature
	make is
	local
		nullt : TYPE
	do
		!!types.make
		!!values.make
		!!expr_trees.make(0,0)
		!!nullt.make("NULL")
		add_type(nullt)
	end
	types : 			HASHED_DICTIONARY[TYPE, STRING]
	values: 			HASHED_DICTIONARY[TYPE, STRING]
	expr_trees :  ARRAY[NODE]
	get_type (n : STRING) : TYPE is
		do
			Result := types.at(n)
		end
	add_expr (e : NODE) is
		do
			expr_trees.add_last(e)
		end
	add_type (t : TYPE) is
		do
			types.put(t, t.name)
		end
	add_value (name : STRING; t : TYPE) is
		do
			values.put(t, name)
		end
	get_value_type (name : STRING) : TYPE is
		do
			Result := values.at(name)
		end

	get_node_type(e : NODE) : TYPE is
		local
			t : FUNCTION_TYPE
			ty : TYPE
			prod : PRODUCT
			seq  : SEQUENCE
		do
			if e.children.count /= 0 then
				if e.value.is_equal("ACCESSP") then
					prod ?= get_node_type(e.children.item(1))
					Result := prod.contents.at(e.children.item(2).value)
				elseif e.value.is_equal("MKPROD") then
					Result := get_type(e.children.item(1).value)
				elseif e.value.is_equal("TREATAS") then
					prod ?= get_type(e.children.item(1).value)
					if prod.contents.has(e.children.item(2).value) then
						Result := prod.contents.at(e.children.item(2).value)
					else
						Result := get_type("NULL")
					end
				elseif e.value.is_equal("EMBED") then
					Result := get_type(e.children.item(1).value)
				elseif e.value.is_equal("HEAD") then
					seq ?= get_node_type(e.children.item(1))
					Result := seq.type_of_sequence;
				elseif e.value.is_equal("TAIL") then
					seq ?= get_node_type(e.children.item(1))
					Result := seq
				elseif e.value.is_equal("CONS") then
					seq ?= get_node_type(e.children.item(2))
					Result := seq
				elseif e.value.is_equal("NIL") then
					Result := get_type(e.children.item(1).value)
				else
					t ?= values.at(e.value)
					Result := t.return
				end
			else
				Result := get_value_type(e.value)
			end

		end

	check_expr(e : NODE) : BOOLEAN is
		local
			t : FUNCTION_TYPE
			s : TYPE
			r : TYPE
			prod : PRODUCT
			i : INTEGER
			field : STRING
			seq  : SEQUENCE
			sum : SUM
			col   : LINKED_LIST[TYPE]
		do
			if e.children.count /= 0 then
				if e.value.is_equal("ACCESSP") then
					Result := check_expr(e.children.item(1))
					prod ?= get_node_type(e.children.item(1))

					if not prod.contents.has(e.children.item(2).value) then
						Result := False
						print("Woah there cowboy, " + prod.name + " does not have a field '" + e.children.item(2).value + ". Check '" + e.dump + "' again.%N%N")
					end
				elseif e.value.is_equal("MKPROD") then
					Result := True
					prod ?= get_type(e.children.item(1).value)
					from i := 2	until i >= e.children.count
					loop
						field := e.children.item(i).value
						s     := get_node_type(e.children.item(i+1))
						if not prod.contents.has(field) then
							Result := False
							print("Woah there cowboy, " + prod.name + " does not have a field '" + field + ". Check '" + e.dump + "' again.")
						elseif s /= prod.contents.at(field) then
							Result := False
							print ("Field '" + field + "' on '" + prod.name + "' is of type '" + prod.contents.at(field).name + "' but given expression is of type '" + s.name + "': '" + e.children.item(i+1).dump + "'%N")
						end
						Result := Result and check_expr(e.children.item(i+1))
						i := i + 2
					end
				elseif e.value.is_equal("TREATAS") then
					Result := check_expr(e.children.item(3))
					sum ?= get_node_type(e.children.item(3))

					if sum = Void then
						Result := False
						print("Seriously? " + e.children.item(3).dump + " is not a sum type. Get your head straight. Jesus. %N")
					else

						field := e.children.item(2).value

						if not sum.contents.has(field) then
							Result := False
							print("Woah there buckaroo, " + sum.name + " does not have a field '" + field + "'. Check '" + e.dump + "' again.%N")
						end
					end
				elseif e.value.is_equal("EMBED") then
					Result := False
					sum ?= get_type(e.children.item(1).value)
					r := get_node_type(e.children.item(2))
					--sum ?= get_node_type(e.children.item(2))
					if sum = Void then
						Result := False
						print("Seriously? " + e.children.item(2).dump + " is not a sum type. Get your head straight. Jesus. %N")
					else
						!!col.make
						sum.contents.item_map_in(col)
						from i := 1 until i > col.count loop
								Result := Result or col.item(i) = r
								i := i + 1
						end
						if not Result then
							print("You need to get your head checked SERIOUSLY ARE YOU RETARDED '" + e.children.item(1).dump + "' does not support type '" + r.name + "'. %N")
						end
						Result := check_expr(e.children.item(2)) and Result
					end
				elseif e.value.is_equal("HEAD") then
					Result := check_expr(e.children.item(1))
				elseif e.value.is_equal("TAIL") then
					Result := check_expr(e.children.item(1))
				elseif e.value.is_equal("CONS") then
					seq ?= get_node_type(e.children.item(2))
					Result := get_node_type(e.children.item(1)) = seq.type_of_sequence
					if not Result then
						print("You are trying to cons a '" + get_node_type(e.children.item(1)).name + "' with a '" + seq.type_of_sequence.name + "'. Stop it. (In: " + e.dump +")%N")
					end
					Result := check_expr(e.children.item(1)) and check_expr(e.children.item(2)) and Result

				elseif e.value.is_equal("NIL") then
					seq ?= get_type(e.children.item(1).value)
					Result := seq /= Void
					if not Result then
						print("Really? Trying to NIL '" + get_type(e.children.item(1).value).name +"'? Does that look like a sequence type? (In: " + e.dump + ")%N")
					end
				else
					t ?= values.at(e.value)
					s := get_node_type(e.children.item(1))
					Result := t.arg.is_super_of( s )
					if not Result then
						print ("Function '" + e.value + "' takes a '" + t.arg.name + "' but given expression is of type '" + s.name + "': '" + e.children.item(1).dump + "'%N")
					end

					Result := Result and check_expr(e.children.item(1));
				end
			else
				Result := True
			end
		end

	check_types is
		local
			i : INTEGER
		do
			from
				i := 1
			until
				i >= expr_trees.count
			loop
				if not check_expr(expr_trees.item(i)) then
					print("Errors in this expression: " + expr_trees.item(i).dump + "%N%N")
				else
					print(expr_trees.item(i).dump + " : " + get_node_type(expr_trees.item(i)).name + "%N%N")
				end
				i := i + 1
			end
		end

end
